/*
 * SPDX-FileCopyrightText: 2023 Mikhail Khachayants
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "helpers/fd_utils.h"
#include <gtest/gtest.h>

#include <pistache/winornix.h>

#include <pistache/common.h>
#include <pistache/eventmeth.h>
#include <pistache/os.h>

#include PIST_SOCKFNS_HDR // e.g. unistd.h

using namespace Pistache;

namespace
{

	class ScopedFd
	{
	public:
		ScopedFd()
			: fd_(PS_FD_EMPTY)
		{
#ifdef _USE_LIBEVENT

			// em_event_new does not allocate an actual fd, so we provide
            // one to achieve the same effect
            em_socket_t actual_fd = PST_SOCK_SOCKET(AF_INET, SOCK_STREAM, 0);

            if (actual_fd < 0)
                throw std::runtime_error("::socket failed");

            fd_ = Polling::Epoll::em_event_new(actual_fd,
                                               EVM_WRITE | EVM_PERSIST,
                                               PST_FD_CLOEXEC,
                                               F_SETFDL_NOTHING);
            if (fd_ == PS_FD_EMPTY)
                throw std::runtime_error("Epoll::em_event_new failed");

#else
			fd_ = ::eventfd(0, EFD_CLOEXEC);
			if (fd_ == PS_FD_EMPTY)
				throw std::runtime_error("::eventfd failed");
#endif
		}

		void close()
		{
			if (fd_ != PS_FD_EMPTY)
			{
				CLOSE_FD(fd_);
				fd_ = PS_FD_EMPTY;
			}
		}

		~ScopedFd()
		{
			close();
		}

	private:
		Fd fd_;
	};

} // namespace

#if defined(_WIN32) && defined(__MINGW32__) && defined(DEBUG)
// In this special case, we allow the number of FDs in use to grow by
    // one. This may be related to the use of GetModuleHandleA to load
    // KernelBase.dll. Or not. We have only seen the number of file handles in
    // use grow in DEBUG mode, so it is also possible it's related to Windows
    // logging.
#define ALLOW_OPEN_FDS_TO_GROW_BY_ONE 1
#endif

TEST(fd_utils_test, same_result_for_two_calls)
{
	// We do an initial log, since the first time something is logged may cause
	// an additional file descriptor to get allocated (no dount refers to the
	// log file) messing up our counting

	PS_LOG_INFO_ARGS("Initial get_open_fds_count %u", get_open_fds_count());

	const auto count1 = get_open_fds_count();
	const auto count2 = get_open_fds_count();

#ifdef ALLOW_OPEN_FDS_TO_GROW_BY_ONE
	if ((count1+1) == count2)
        ASSERT_EQ(count1+1, count2);
    else
#endif
	ASSERT_EQ(count1, count2);
}

TEST(fd_utils_test, delect_new_descriptor)
{
	const auto count1 = get_open_fds_count();
	const ScopedFd new_fd;
	const auto count2 = get_open_fds_count();
#ifdef _WIN32
	// Doing a winsock "socket" call to allocate a socket handle actually seems
	// to use up 7 handles in total (Windows 11, Sept/2024)
	ASSERT_GT(count2, count1);
	ASSERT_GT(count1+32, count2);
#else
	ASSERT_EQ(count1 + 1, count2);
#endif
}

TEST(fd_utils_test, delect_descriptor_close)
{
	ScopedFd fd;
	const auto count1 = get_open_fds_count();
	fd.close();
	const auto count2 = get_open_fds_count();

#ifdef ALLOW_OPEN_FDS_TO_GROW_BY_ONE
	if (count1 == count2)
        ASSERT_EQ(count1, count2);
    else
#endif
	ASSERT_EQ(count1, count2 + 1);
}
